import { GridLayout, CircularLayout, DagreLayout } from "@antv/layout"
import Main from "../main"
import { Point,Edge,Node } from "@antv/x6"
import { ref } from "vue";
import { IPackAntvX6 } from "../inter";

type F = (d:any) => any;

export interface Model {
    nodes?: Node[];
    edges?: Edge[];
    // combos?: Combo[];
    comboEdges?: Edge[];
    hiddenNodes?: Node[];
    hiddenEdges?: Edge[];
    // hiddenCombos?: Combo[];
    vedges?: Edge[];
}

export default (main: Main) => {
    const name = ref<IPackAntvX6.LayoutNames>('self')
    const icons = {self:'i-fluent:grid-20-regular',grid:'i-fluent:grid-dots-24-filled',circular:'i-mdi:selection-ellipse',dagre:'i-carbon:skill-level-advanced',ellipse:'i-mdi:ellipse-outline'}
    const layout:{grid:F|undefined,circular:F|undefined,dagre:F|undefined,ellipse:F|undefined} = {grid:undefined,circular:undefined,dagre:undefined,ellipse:undefined}
    const excludeIds:string[] = [];
    //
    const resetExcludeIds = (ids:string[]) => {
        excludeIds.length = 0;
        excludeIds.push(...ids)
    }
    const addExcludeIds = (ids:string[]) => {
        excludeIds.push(...ids)
    }
    const clearExcludeIds = () => {
        excludeIds.length = 0;
    }
    //
    const layoutFn = (m:Model) => {
        const nodes = m.nodes;
        if(!nodes || nodes.length == 0) return [];
        let f:any = undefined;
        switch(name.value){
            case "grid":f = grid;break;
            case "circular":f = circular;break;
            case "dagre":f = dagre;break;
            case "ellipse":f = ellipse;break;
            default:f = () => ({nodes});
        }
        //
        const res:any =  f(m);
        // console.log(res,nodes,name.value)
        return res.nodes || [];
    }

    const getSize = () => {
        return [main.graph.containerEl.offsetWidth, main.graph.containerEl.offsetHeight]
    }
    const getData = (m:Model) => {
        return {
            nodes:m.nodes?.filter(d => !excludeIds.includes(d.id)).map(n => n?.toJSON?.() || n),
            edges:m.edges?.map(n => n?.toJSON?.() || n),
        }
    }
    //  网格布局
    const grid = (m:Model, options?: any) => {
        const md = getData(m)
        if(options == undefined && layout.grid != undefined){
            return layout.grid(md)
        }
        const [width, height] = getSize();
        const ops: any = Object.assign({ sortBy: "label", nodeSize: [200, 80], type: "grid", width, height ,rows:5,cols:5 }, options || {})
        const base = new GridLayout(ops);
        layout.grid = (m:any) => base.layout(m)
        return layout.grid(md);
    }
    // 环形布局
    const circular = (m:Model, options?: any) => {
        const md = getData(m)
        if(options == undefined && layout.circular != undefined){
            return layout.circular(md)
        }
        const center = getSize().map(n => n / 2);
        const ops: any = Object.assign({ type: 'circular', center }, options || {});
        const base = new CircularLayout(ops);
        layout.circular = (m:any) => base.layout(m);
        return layout.circular(md);
    }
    // 层次布局
    const dagre = (m:Model, options?: any) => {
        const md = getData(m)
        if(options == undefined && layout.dagre != undefined){
            return layout.dagre(md)
        }
        const ops = Object.assign({ type: 'dagre', rankdir: 'LR', align: 'UR', ranksep: 120, nodesep: 15 }, options || {})
        const base = new DagreLayout(ops);
        layout.dagre = (m:any) => base.layout(m)
        return layout.dagre(md);
    }

    // 椭圆布局
    const ellipse = (m:Model, options?: {center:[number,number],axis:[number,number]}) => {
        const md = getData(m)
        const ns = md.nodes || [];
        if(options == undefined && layout.ellipse != undefined){
            return layout.ellipse({nodes:ns})
        }
        const s = getSize();
        const [cx, cy] = options?.center || s.map(n => n / 2)
        const [rx, ry] = options?.axis || [cx * 0.7, cy * 0.7];
        const ratio = rx / ry;
        const center = new Point(cx, cy);
        const start = new Point(cx, cy - ry);
        
        //
        layout.ellipse = (model:{nodes:any[]}) => {
            const stepAngle = 360 / model.nodes.length;
            const ts = model.nodes.map((node,i) => {
                const angle = stepAngle * i;
                const p = start
                    .clone()
                    .rotate(-angle, center)
                    .scale(ratio, 1, center)
                    .round()
                node.x = p.x;
                node.y = p.y;
                return node;
            })
            return {nodes:ts}
        }

        return layout.ellipse({nodes:ns})
    }
    //
    const execute = () => {
        main.graph.layout(layoutFn)
    }

    main.graph.onMounted((graph) => {
        graph.on('node:added',() => {
            if(name.value != 'self'){
                execute();
            }
        })
    })

    return {
        name,
        icons,
        grid,
        circular,
        dagre,
        ellipse,
        layoutFn,
        execute,
        resetExcludeIds,
        addExcludeIds,
        clearExcludeIds,
    }

}